﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using Turquoise.Core.Abstract;
using Turquoise.DataAccess.Abstract;
using Turquoise.DataAccess.EntityFramework.Abstract;
using Turquoise.DataAccess.Helpers;

namespace Turquoise.DataAccess.EntityFramework
{
    public class Repository<T> : IRepository<T> where T : class, IEntity, new()
    {
        private readonly IDatabaseContext _context;
        private bool _disposed;
        private IDbSet<T> TableCollection => _context.Set<T>();
        public Repository(IDatabaseContext context)
        {
            _context = context;
        }
        public IQueryable<T> DbSet()
        {
            return TableCollection;
        }
        public T Add(T entity)
        {
            _context.Set<T>().Add(entity);
            _context.SaveChanges();
            return entity;
        }
        public T Update(T entity)
        {
            _context.Entry(entity).State = EntityState.Modified;
            _context.SaveChanges();
            return entity;
        }
        public void Delete(T entity)
        {
            if (_context.Entry(entity).State == EntityState.Detached)
            {
                _context.Set<T>().Attach(entity);
            }
            _context.Set<T>().Remove(entity);
            _context.SaveChanges();
        }

        public void Delete(Expression<Func<T, bool>> whereExpression)
        {
            Delete(Get(whereExpression));
        }

        public virtual T Get(Expression<Func<T, bool>> whereExpression)
        {
            return _context.Set<T>().Where(whereExpression).FirstOrDefault();
        }
        public virtual T Get(Expression<Func<T, object>>[] includeExpressions, Expression<Func<T, bool>> whereExpression)
        {
            IQueryable<T> query = _context.Set<T>();
            query = includeExpressions.Aggregate(query, (current, include) => current.Include(include));
            return query.Where(whereExpression).FirstOrDefault();
        }
        public virtual List<T> GetList()
        {
            return _context.Set<T>().ToList();
        }
        public virtual List<T> GetList(Expression<Func<T, bool>> whereExpression)
        {
            return _context.Set<T>().Where(whereExpression).ToList();
        }
        public List<T> GetList(Expression<Func<T, object>>[] includeExpressions, Expression<Func<T, bool>> whereExpression)
        {
            IQueryable<T> query = _context.Set<T>();
            query = includeExpressions.Aggregate(query, (current, include) => current.Include(include));
            return query.Where(whereExpression).ToList();
        }
        public virtual List<T> GetList(Expression<Func<T, bool>> whereExpression, SortHelper<T> sortHelper)
        {
            var query = (IOrderedQueryable<T>) _context.Set<T>().Where(whereExpression);
            query = sortHelper.GenerateOrderedQuery(query);
            return query.ToList();
        }
        public List<T> GetList(Expression<Func<T, object>>[] includeExpressions, Expression<Func<T, bool>> whereExpression, SortHelper<T> sortHelper)
        {
            var query = (IOrderedQueryable<T>)_context.Set<T>();
            query = (IOrderedQueryable<T>)includeExpressions.Aggregate(query, (current, include) => (IOrderedQueryable<T>)current.Include(include)).Where(whereExpression);
            query = sortHelper.GenerateOrderedQuery(query);
            return query.ToList();
        }
        public virtual List<T> GetList(Expression<Func<T, bool>> whereExpression, SortHelper<T> sortHelper, int index, int size)
        {
            var query = (IOrderedQueryable<T>) _context.Set<T>().Where(whereExpression);
            query = sortHelper.GenerateOrderedQuery(query);
            return query.Skip((index - 1) * size).Take(size).ToList();
        }
        public List<T> GetList(Expression<Func<T, object>>[] includeExpressions, Expression<Func<T, bool>> whereExpression, SortHelper<T> sortHelper, int index, int size)
        {
            var query = (IOrderedQueryable<T>) _context.Set<T>();
            query = (IOrderedQueryable<T>)includeExpressions.Aggregate(query, (current, include) => (IOrderedQueryable<T>)current.Include(include)).Where(whereExpression);
            query = sortHelper.GenerateOrderedQuery(query);
            return query.Skip((index - 1) * size).Take(size).ToList();
        }
        public int GetCount(Expression<Func<T, bool>> whereExpression)
        {
            var x = _context.Set<T>().Where(whereExpression).Count();
            return x;
        }
        public int GetCount(Expression<Func<T, object>>[] includeExpressions, Expression<Func<T, bool>> whereExpression)
        {
            IQueryable<T> query = _context.Set<T>();
            query = includeExpressions.Aggregate(query, (current, include) => current.Include(include));
            return query.Where(whereExpression).Count();
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            _disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

    }
}